package service

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/dao"
	"core/models/db"
	"core/models/entity"
	"core/repo"
	"fmt"
	"framework/msError"
	"hall/models/request"
	"hall/models/response"
	"strconv"
	"time"
)

type MarketService struct {
	typeDao      *dao.TypeDao
	marketDao    *dao.MarketDao
	warehouseDao *dao.WarehouseDao
	confDao      *dao.ConfigDao
}

func (m *MarketService) MarketSell(ctx context.Context, uid uint, req *request.MarketSell) (*entity.TradeSellLog, *msError.Error) {
	marketSellTypeIDStr := m.confDao.GetVal(ctx, "market_sell_type_id")
	marketSellTypeIDInt, _ := strconv.Atoi(marketSellTypeIDStr)
	marketSellTypeID := uint(marketSellTypeIDInt)
	SellTypes := m.typeDao.GetInfo(ctx, marketSellTypeID)
	if SellTypes == nil || SellTypes.ID == 0 {
		SellTypes = &db.Type{}
		//logs.Error("[marketHandler]用户%d MarketSell err:售卖类型未配置，无数据 req=%v", uid, req)
		//return nil, biz.RequestDataError
	}

	types := m.typeDao.GetInfo(ctx, req.TypeID)
	if types == nil {
		logs.Error("[marketHandler]用户%d MarketSell err:获取类型数据失败 req=%v", uid, req)
		return nil, biz.NotTypeError
	}
	if !types.IsSell {
		logs.Error("[marketHandler]用户%d MarketSell err:该物品不可售卖 req=%v", uid, types)
		return nil, biz.MarketNotSellError
	}
	if req.Price < 0.01 {
		logs.Error("[marketHandler]用户%d MarketSell err:单价小于0.01 req=%v", uid, req)
		return nil, biz.SellLessThanMinPriceError
	}
	if types.PriceMaxLimit > 0 {
		var maxPriceFloat float64
		if marketSellTypeID == 11 {
			maxPriceFloat = utils.DecimalIntDiv(types.PriceMaxLimit, SellTypes.Val)
		} else {
			maxPriceFloat = utils.DecimalIntDiv(types.PriceMaxLimit, 100)
		}
		if req.Price > maxPriceFloat {
			logs.Error("[marketHandler]用户%d MarketSell err:售卖%f,超过最大售卖价%f req=%v", uid, req.Price, maxPriceFloat, req)
			return nil, biz.SellLimitationError
		}
	}

	//max_price
	materialLog := new(entity.MaterialLog)
	num := req.Num
	switch req.Type {
	case 1:
		req.Num = 1
		info := m.warehouseDao.GetCardInfo(ctx, uid, req.ID)
		if info == nil || info.ID == 0 {
			logs.Error("[marketHandler]用户%d MarketSell err:获取卡牌数据失败 req=%v", uid, req)
			return nil, biz.MarketNotSellError
		}
		if info.TypeID != req.TypeID {
			logs.Error("[marketHandler]用户%d MarketSell err:获取卡牌数据失败 req=%v", uid, req)
			return nil, biz.NotTypeError
		}
		if info.IsUse || !info.IsNew {
			logs.Error("[marketHandler]用户%d MarketSell err:状态不符合售卖 req=%v", uid, info)
			return nil, biz.UseORSellError
		}
	case 2:
		if req.Num < 1 {
			logs.Error("[marketHandler]用户%d MarketSell err:数量小于1 req=%v", uid, req)
			return nil, biz.Fail
		}
		info := m.warehouseDao.GetMaterialInfo(ctx, uid, req.ID)
		if info == nil || info.ID == 0 {
			logs.Error("[marketHandler]用户%d MarketSell err:获取材料数据失败 req=%v", uid, req)
			return nil, biz.MarketNotSellError
		}
		if info.TypeID != req.TypeID {
			logs.Error("[marketHandler]用户%d MarketSell err:获取材料数据失败 req=%v", uid, req)
			return nil, biz.NotTypeError
		}
		if info.TypeID == 11 {
			num = utils.DecimalFloatByInt(utils.DecimalFloatMul(utils.DecimalIntByFloat(req.Num), utils.DecimalIntByFloat(info.Types.Val)))
		}
		if req.Num > info.Num {
			logs.Error("[marketHandler]用户%d MarketSell err:售卖数量%d，大于余数%d req=%v", uid, req.Num, info.Num, info)
			return nil, biz.NotEnoughGold
		}
		materialLog = &entity.MaterialLog{
			UID:        uid,
			WID:        req.ID,
			TypeID:     req.TypeID,
			TypeName:   info.Types.Name,
			TypeImg:    info.Types.Img,
			TypeModule: info.Types.Module,
			Ago:        info.Num,
			Num:        -num,
			After:      info.Num - num,
			Remarks:    "市场-挂售材料",
			AddTime:    time.Now().Format(time.DateTime),
		}
	case 3:
		req.Num = 1
		info := m.warehouseDao.GetEquipInfo(ctx, uid, req.ID)
		if info == nil || info.ID == 0 {
			logs.Error("[marketHandler]用户%d MarketSell err:获取装备数据失败 req=%v", uid, req)
			return nil, biz.MarketNotSellError
		}
		if info.TypeID != req.TypeID {
			logs.Error("[marketHandler]用户%d MarketSell err:获取装备数据失败 req=%v", uid, req)
			return nil, biz.NotTypeError
		}
		if info.IsUse || !info.IsNew {
			logs.Error("[marketHandler]用户%d MarketSell err:状态不符合售卖 req=%v", uid, info)
			return nil, biz.UseORSellError
		}
	}
	var price = req.Price
	if marketSellTypeID == 11 {
		price = utils.DecimalFloatMul(price, utils.DecimalIntByFloat(SellTypes.Val))
	}
	tradeLog := &entity.TradeSellLog{
		OID:          0,
		UID:          uid,
		WID:          req.ID,
		Types:        req.Type,
		TypeID:       req.TypeID,
		TypeName:     types.Name,
		TypeImg:      types.Img,
		SellTypeID:   SellTypes.ID,
		SellTypeName: SellTypes.Name,
		SellTypeImg:  SellTypes.Img,
		Price:        price,
		TotalPrice:   utils.DecimalFloatMul(price, utils.DecimalIntByFloat(req.Num)),
		Num:          num,
		Status:       entity.TradeNone,
		SellTime:     time.Now().Unix(),
	}
	trade := &db.Trade{
		UID:        uid,
		WID:        req.ID,
		Type:       req.Type,
		TypeID:     req.TypeID,
		SellTypeID: marketSellTypeID,
		Price:      tradeLog.Price,
		TotalPrice: tradeLog.TotalPrice,
		Num:        num,
		SyNum:      num,
	}
	err := new(msError.Error)
	tradeLog, err = m.marketDao.Sell(ctx, tradeLog, trade, materialLog)
	if err != nil {
		return nil, err
	}
	return tradeLog, nil
}

func (m *MarketService) MarketSellList(ctx context.Context, req *request.MarketSearch, uid uint) (int64, *[]response.TradeSellList) {
	typeIn := m.typeDao.GetTypesIn(ctx, req.Type, req.TypeTwo)
	count, data := m.marketDao.SellAll(ctx, req.Page, req.PageSize, typeIn, uid)
	var list []response.TradeSellList
	for _, v := range *data {
		syNum := v.SyNum
		price := v.Price
		total := v.TotalPrice
		var typeLevel int64
		if v.TypeID == 11 {
			syNum = utils.DecimalFloatByInt(utils.DecimalFloatDiv(utils.DecimalIntByFloat(syNum), utils.DecimalIntByFloat(v.Types.Val)))
		}
		if v.Types.Type == 1 {
			card := m.warehouseDao.GetMarketCardInfo(ctx, v.UID, v.WID)
			typeLevel = card.TypeCard.Level
		}
		if v.SellTypeID == 11 {
			price = utils.DecimalFloatDiv(price, utils.DecimalIntByFloat(v.SellTypes.Val))
			total = utils.DecimalFloatDiv(total, utils.DecimalIntByFloat(v.SellTypes.Val))
		}
		info := response.TradeSellList{
			OID:          v.ID,
			Type:         v.Types.Type,
			TypeTwo:      v.Types.Types,
			TypeID:       v.TypeID,
			TypeName:     v.Types.Name,
			TypeImg:      v.Types.Img,
			TypeType:     v.Types.Type,
			TypeLevel:    typeLevel,
			SellTypeID:   v.SellTypeID,
			SellTypeName: v.SellTypes.Name,
			SellTypeImg:  v.SellTypes.Img,
			Price:        price,
			TotalPrice:   total,
			SyNum:        syNum,
			SellTime:     fmt.Sprintf("%v", v.CreatedAt.Format(time.DateTime)),
		}
		list = append(list, info)
	}
	return count, &list
}

func (m *MarketService) MarketOrderInfo(ctx context.Context, cancel int, uid uint, id uint) *entity.MarketOrder {
	order := m.marketDao.MongoGetOrder(ctx, cancel, uid, id)
	return order
}

func (m *MarketService) GetTrade(ctx context.Context, id uint) *db.TradeHasOneType {
	trade := m.marketDao.GetTrade(ctx, id)
	if trade.Status != 0 {
		return nil
	}
	return trade
}

func (m *MarketService) MarketOrderGen(ctx context.Context, order *entity.MarketOrder) (*entity.MarketOrder, *msError.Error) {
	return m.marketDao.MarketCreateOrder(ctx, order)
}

func (m *MarketService) MarketOrderCancel(ctx context.Context, order *entity.MarketOrder) error {
	order.PayStatus = entity.TradePayCancel
	order.Cancel = time.Now().Unix()
	return m.marketDao.MarketOrderCancel(ctx, order)
}

func (m *MarketService) MarketMySellList(ctx context.Context, req *request.PageMsg, uid uint) (int64, *[]response.TradeMySellList) {
	count, data := m.marketDao.MySellAll(ctx, req.Page, req.PageSize, uid)
	var list []response.TradeMySellList
	for _, v := range *data {
		if v.TypeID == 11 {
			v.Num = utils.DecimalFloatByInt(utils.DecimalIntDiv(v.Num, v.Types.Val))
			v.SalesNum = utils.DecimalFloatByInt(utils.DecimalIntDiv(v.SalesNum, v.Types.Val))
			v.SyNum = utils.DecimalFloatByInt(utils.DecimalIntDiv(v.SyNum, v.Types.Val))
			v.ReturnNum = utils.DecimalFloatByInt(utils.DecimalIntDiv(v.ReturnNum, v.Types.Val))
		}
		if v.SellTypeID == 11 {
			v.Price = utils.DecimalFloatDiv(v.Price, utils.DecimalIntByFloat(v.SellTypes.Val))
		}
		var typeLevel int64
		if v.Types.Type == 1 {
			card := m.warehouseDao.GetMarketCardInfo(ctx, v.UID, v.WID)
			typeLevel = card.TypeCard.Level
		}
		order := m.marketDao.MongoMarketOrder(ctx, v.ID)
		var orderList []response.TradeOrder
		for _, val := range *order {
			if v.TypeID == 11 {
				val.Num = utils.DecimalFloatByInt(utils.DecimalIntDiv(val.Num, v.Types.Val))
			}
			var payTime string
			if val.PayTime > 0 {
				payTime = utils.TimeAsDate(val.PayTime)
			}
			if val.SellTypeID == 11 {
				val.TotalPrice = utils.DecimalFloatDiv(val.TotalPrice, utils.DecimalIntByFloat(v.SellTypes.Val))
				val.Fee = utils.DecimalFloatDiv(val.Fee, utils.DecimalIntByFloat(v.SellTypes.Val))
			}
			orderList = append(orderList, response.TradeOrder{
				BuyTime:   utils.TimeAsDate(val.CreateTime),
				OrderNo:   val.OrderNo,
				Num:       val.Num,
				Fee:       val.Fee,
				PayMoney:  utils.DecimalFloatSub(val.TotalPrice, val.Fee),
				PayStatus: int(val.PayStatus),
				PayTime:   payTime,
				Cancel:    utils.TimeAsDate(val.Cancel),
			})
		}
		listOne := response.TradeMySellList{
			GID:            v.ID,
			TypeID:         v.TypeID,
			TypeName:       v.Types.Name,
			TypeImg:        v.Types.Img,
			TypeLevel:      typeLevel,
			TypeType:       v.Types.Type,
			SellTypeID:     v.SellTypeID,
			SellTypeName:   v.SellTypes.Name,
			SellTypeImg:    v.SellTypes.Img,
			Price:          v.Price,
			Num:            v.Num,
			SyNum:          v.SyNum,
			TransactionNum: v.Num - v.SyNum - v.SalesNum - v.ReturnNum,
			SalesNum:       v.SalesNum,
			ReturnNum:      v.ReturnNum,
			Status:         v.Status,
			OrderAll:       &orderList,
			SellTime:       fmt.Sprintf("%v", v.CreatedAt.Format(time.DateTime)),
		}
		list = append(list, listOne)
	}
	return count, &list
}

func (m *MarketService) MarketSellWithdraw(ctx context.Context, uid uint, req *request.CommonID) (*response.TradeMySellList, *msError.Error) {
	trade := m.marketDao.GetTrade(ctx, req.ID)
	if trade == nil || trade.ID == 0 {
		logs.Error("[marketHandler]用户%d MarketSellWithdraw err:获取售卖数据失败 req=%v", uid, req)
		return nil, biz.RequestDataError
	}
	if trade.UID != uid {
		logs.Error("[marketHandler]用户%d MarketSellWithdraw err:获取售卖数据失败 req=%v", uid, req)
		return nil, biz.SqlError
	}
	trade.Status = 3
	if trade.SyNum <= 0 {
		logs.Error("[marketHandler]用户%d MarketSellWithdraw err:剩余数量不足，不可下架 req=%v", uid, trade)
		return nil, biz.SqlError
	}
	trade.ReturnNum += trade.SyNum
	trade.SyNum = 0
	tradeLog := &entity.TradeSellLog{
		OID:          trade.ID,
		UID:          uid,
		WID:          trade.WID,
		Types:        trade.Type,
		TypeID:       trade.TypeID,
		TypeName:     trade.Types.Name,
		TypeImg:      trade.Types.Img,
		SellTypeID:   trade.SellTypeID,
		SellTypeName: trade.SellTypes.Name,
		SellTypeImg:  trade.SellTypes.Img,
		Price:        trade.Price,
		TotalPrice:   utils.DecimalFloatMul(trade.Price, utils.DecimalIntByFloat(trade.Num)),
		Num:          trade.Num,
		Status:       entity.TradeWithdraw,
		SellTime:     time.Now().Unix(),
	}
	materialLog := new(entity.MaterialLog)
	if trade.Types.Type == 2 {
		info := m.warehouseDao.GetMaterialInfo(ctx, uid, trade.WID)
		materialLog = &entity.MaterialLog{
			UID:        uid,
			WID:        trade.WID,
			TypeID:     trade.TypeID,
			TypeName:   info.Types.Name,
			TypeImg:    info.Types.Img,
			TypeModule: info.Types.Module,
			LinkID:     fmt.Sprintf("%d", trade.WID),
			LinkTable:  trade.TableName(),
			Ago:        info.Num,
			Num:        trade.ReturnNum,
			After:      info.Num + trade.ReturnNum,
			Remarks:    "市场-下架材料",
			AddTime:    time.Now().Format(time.DateTime),
		}
	}
	err := new(msError.Error)
	trade, err = m.marketDao.MarketSellWithdraw(ctx, uid, req.ID, trade, tradeLog, materialLog)
	if err != nil {
		return nil, err
	}
	order := m.marketDao.MongoMarketOrder(ctx, trade.ID)
	var orderList []response.TradeOrder
	for _, val := range *order {
		var payTime string
		if val.PayTime > 0 {
			payTime = utils.TimeAsDate(val.PayTime)
		}
		orderList = append(orderList, response.TradeOrder{
			BuyTime:   utils.TimeAsDate(val.CreateTime),
			OrderNo:   val.OrderNo,
			Num:       val.Num,
			Fee:       val.Fee,
			PayMoney:  trade.Price*float64(val.Num) - val.Fee,
			PayStatus: int(val.PayStatus),
			PayTime:   payTime,
		})
	}
	res := response.TradeMySellList{
		GID:            trade.ID,
		TypeID:         trade.TypeID,
		TypeName:       trade.Types.Name,
		TypeImg:        trade.Types.Img,
		Price:          trade.Price,
		Num:            trade.Num,
		SyNum:          trade.SyNum,
		TransactionNum: trade.Num - trade.SyNum - trade.SalesNum - trade.ReturnNum,
		SalesNum:       trade.SalesNum,
		ReturnNum:      trade.ReturnNum,
		Status:         trade.Status,
		OrderAll:       &orderList,
		SellTime:       fmt.Sprintf("%v", trade.CreatedAt.Format(time.DateTime)),
	}
	return &res, nil
}

func (m *MarketService) MarketMyBuyList(ctx context.Context, uid uint, req *request.MarketBuySearch) (int64, *[]entity.MarketOrder) {
	count, data := m.marketDao.MarketOrderList(ctx, uid, &req.PageMsg, req.Status)
	res := *data
	for k, v := range res {
		if v.TypeID == 11 {
			types := m.typeDao.GetInfo(ctx, v.TypeID)
			v.Num = utils.DecimalFloatByInt(utils.DecimalIntDiv(v.Num, types.Val))
		}
		if v.SellTypeID == 11 {
			sellTypes := m.typeDao.GetInfo(ctx, v.SellTypeID)
			v.Price = utils.DecimalFloatDiv(v.Price, utils.DecimalIntByFloat(sellTypes.Val))
			v.TotalPrice = utils.DecimalFloatDiv(v.TotalPrice, utils.DecimalIntByFloat(sellTypes.Val))
			v.Fee = utils.DecimalFloatDiv(v.Fee, utils.DecimalIntByFloat(sellTypes.Val))
		}
		res[k] = v
	}
	return count, &res
}

func (m *MarketService) MarketCancel(ctx context.Context, uid uint, orderNo string) (*entity.MarketOrder, *msError.Error) {
	order := m.marketDao.MongoGetMarketOrderByOrderNo(ctx, uid, orderNo)
	order.PayStatus = entity.TradePayCancel
	order.Cancel = time.Now().Unix()
	err := m.marketDao.MarketOrderCancel(ctx, order)
	if err != nil {
		logs.Error("[marketHandler]用户%d MarketCancel err:取消购买失败 req=%v,dbErr:%v", uid, order, err)
		return nil, biz.SqlError
	}
	return order, nil
}

func (m *MarketService) OrderInfoByOrderNo(ctx context.Context, uid uint, orderNo string) *entity.MarketOrder {
	return m.marketDao.MongoGetOrderByOrderNo(ctx, uid, orderNo)
}

func (m *MarketService) TradePay(ctx context.Context, order *entity.MarketOrder, response string) error {
	return m.marketDao.OrderPay(ctx, order, response)
}

func (m *MarketService) MarketBuyList(ctx context.Context) *[]entity.MarketOrder {
	return m.marketDao.MarketBuyList(ctx)
}

func (m *MarketService) GetConfInfo(ctx context.Context, name string) float64 {
	val := m.confDao.GetVal(ctx, name)
	return utils.DecimalStringByFloat(val)
}

func NewMarketService(r *repo.Manager) *MarketService {
	return &MarketService{
		typeDao:      dao.NewTypeDao(r),
		marketDao:    dao.NewMarketDao(r),
		warehouseDao: dao.NewWarehouseDao(r),
		confDao:      dao.NewConfigDao(r),
	}
}
